{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# Sample Python code to analyze\n",
    "sample_code = r\"\"\"\n",
    "def e2e_eval(gt_dir, res_dir, ignore_blank=False):\n",
    "    print('start testing...')\n",
    "    iou_thresh = 0.5\n",
    "    val_names = os.listdir(gt_dir)\n",
    "    num_gt_chars = 0\n",
    "    gt_count = 0\n",
    "    dt_count = 0\n",
    "    hit = 0\n",
    "    ed_sum = 0\n",
    "    for i, val_name in enumerate(val_names):\n",
    "        with open(os.path.join(gt_dir, val_name), encoding='utf-8') as f:\n",
    "            gt_lines = [o.strip() for o in f.readlines()]\n",
    "        gts = []\n",
    "        ignore_masks = []\n",
    "        for line in gt_lines:\n",
    "            parts = line.strip().split('\\t')\n",
    "            # ignore illegal data\n",
    "            if len(parts) < 9:\n",
    "                continue\n",
    "            assert (len(parts) < 11)\n",
    "            if len(parts) == 9:\n",
    "                gts.append(parts[:8] + [''])\n",
    "            else:\n",
    "                gts.append(parts[:8] + [parts[-1]])\n",
    "            ignore_masks.append(parts[8])\n",
    "        val_path = os.path.join(res_dir, val_name)\n",
    "        if not os.path.exists(val_path):\n",
    "            dt_lines = []\n",
    "        else:\n",
    "            with open(val_path, encoding='utf-8') as f:\n",
    "                dt_lines = [o.strip() for o in f.readlines()]\n",
    "        dts = []\n",
    "        for line in dt_lines:\n",
    "            # print(line)\n",
    "            parts = line.strip().split(\"\\t\")\n",
    "            assert (len(parts) < 10), \"line error: {}\".format(line)\n",
    "            if len(parts) == 8:\n",
    "                dts.append(parts + [''])\n",
    "            else:\n",
    "                dts.append(parts)\n",
    "        dt_match = [False] * len(dts)\n",
    "        gt_match = [False] * len(gts)\n",
    "        all_ious = defaultdict(tuple)\n",
    "        for index_gt, gt in enumerate(gts):\n",
    "            gt_coors = [float(gt_coor) for gt_coor in gt[0:8]]\n",
    "            gt_poly = polygon_from_str(gt_coors)\n",
    "            for index_dt, dt in enumerate(dts):\n",
    "                dt_coors = [float(dt_coor) for dt_coor in dt[0:8]]\n",
    "                dt_poly = polygon_from_str(dt_coors)\n",
    "                iou = polygon_iou(dt_poly, gt_poly)\n",
    "                if iou >= iou_thresh:\n",
    "                    all_ious[(index_gt, index_dt)] = iou\n",
    "        sorted_ious = sorted(\n",
    "            all_ious.items(), key=operator.itemgetter(1), reverse=True)\n",
    "        sorted_gt_dt_pairs = [item[0] for item in sorted_ious]\n",
    "\n",
    "        # matched gt and dt\n",
    "        for gt_dt_pair in sorted_gt_dt_pairs:\n",
    "            index_gt, index_dt = gt_dt_pair\n",
    "            if gt_match[index_gt] == False and dt_match[index_dt] == False:\n",
    "                gt_match[index_gt] = True\n",
    "                dt_match[index_dt] = True\n",
    "                if ignore_blank:\n",
    "                    gt_str = strQ2B(gts[index_gt][8]).replace(\" \", \"\")\n",
    "                    dt_str = strQ2B(dts[index_dt][8]).replace(\" \", \"\")\n",
    "                else:\n",
    "                    gt_str = strQ2B(gts[index_gt][8])\n",
    "                    dt_str = strQ2B(dts[index_dt][8])\n",
    "                if ignore_masks[index_gt] == '0':\n",
    "                    ed_sum += ed(gt_str, dt_str)\n",
    "                    num_gt_chars += len(gt_str)\n",
    "                    if gt_str == dt_str:\n",
    "                        hit += 1\n",
    "                    gt_count += 1\n",
    "                    dt_count += 1\n",
    "\n",
    "        # unmatched dt\n",
    "        for tindex, dt_match_flag in enumerate(dt_match):\n",
    "            if dt_match_flag == False:\n",
    "                dt_str = dts[tindex][8]\n",
    "                gt_str = ''\n",
    "                ed_sum += ed(dt_str, gt_str)\n",
    "                dt_count += 1\n",
    "\n",
    "        # unmatched gt\n",
    "        for tindex, gt_match_flag in enumerate(gt_match):\n",
    "            if gt_match_flag == False and ignore_masks[tindex] == '0':\n",
    "                dt_str = ''\n",
    "                gt_str = gts[tindex][8]\n",
    "                ed_sum += ed(gt_str, dt_str)\n",
    "                num_gt_chars += len(gt_str)\n",
    "                gt_count += 1\n",
    "\n",
    "    eps = 1e-9\n",
    "    print('hit, dt_count, gt_count', hit, dt_count, gt_count)\n",
    "    precision = hit / (dt_count + eps)\n",
    "    recall = hit / (gt_count + eps)\n",
    "    fmeasure = 2.0 * precision * recall / (precision + recall + eps)\n",
    "    avg_edit_dist_img = ed_sum / len(val_names)\n",
    "    avg_edit_dist_field = ed_sum / (gt_count + eps)\n",
    "    character_acc = 1 - ed_sum / (num_gt_chars + eps)\n",
    "    print('character_acc: %.2f' % (character_acc * 100) + \"%\")\n",
    "    print('avg_edit_dist_field: %.2f' % (avg_edit_dist_field))\n",
    "    print('avg_edit_dist_img: %.2f' % (avg_edit_dist_img))\n",
    "    print('precision: %.2f' % (precision * 100) + \"%\")\n",
    "    print('recall: %.2f' % (recall * 100) + \"%\")\n",
    "    print('fmeasure: %.2f' % (fmeasure * 100) + \"%\")\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0,\n",
       " 0,\n",
       " 3,\n",
       " 3,\n",
       " 4,\n",
       " 5,\n",
       " 6,\n",
       " 7,\n",
       " 8,\n",
       " 9,\n",
       " 10,\n",
       " 10,\n",
       " 10,\n",
       " 11,\n",
       " 12,\n",
       " 13,\n",
       " 14,\n",
       " 15,\n",
       " 0,\n",
       " 15,\n",
       " 15,\n",
       " 15,\n",
       " 15,\n",
       " 15,\n",
       " 0,\n",
       " 15,\n",
       " 15,\n",
       " 14,\n",
       " 14,\n",
       " 15,\n",
       " 0,\n",
       " 15,\n",
       " 15,\n",
       " 16,\n",
       " 17,\n",
       " 0,\n",
       " 18,\n",
       " 18,\n",
       " 18,\n",
       " 18,\n",
       " 0,\n",
       " 18,\n",
       " 17,\n",
       " 18,\n",
       " 19,\n",
       " 19,\n",
       " 20,\n",
       " 21,\n",
       " 21,\n",
       " 22,\n",
       " 23,\n",
       " 24,\n",
       " 24,\n",
       " 24,\n",
       " 20,\n",
       " 20,\n",
       " 21,\n",
       " 0,\n",
       " 0,\n",
       " 22,\n",
       " 22,\n",
       " 22,\n",
       " 22,\n",
       " 22,\n",
       " 22,\n",
       " 23,\n",
       " 24,\n",
       " 0,\n",
       " 24,\n",
       " 24,\n",
       " 24,\n",
       " 24,\n",
       " 24,\n",
       " 24,\n",
       " 24,\n",
       " 24,\n",
       " 24,\n",
       " 0,\n",
       " 0,\n",
       " 21,\n",
       " 21,\n",
       " 22,\n",
       " 23,\n",
       " 23,\n",
       " 23,\n",
       " 0,\n",
       " 0,\n",
       " 21,\n",
       " 21,\n",
       " 22,\n",
       " 23,\n",
       " 23,\n",
       " 23,\n",
       " 23,\n",
       " 0,\n",
       " 11,\n",
       " 11,\n",
       " 12,\n",
       " 13,\n",
       " 14,\n",
       " 15,\n",
       " 16,\n",
       " 17,\n",
       " 17,\n",
       " 17,\n",
       " 17,\n",
       " 17,\n",
       " 17,\n",
       " 17,\n",
       " 0]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import ast\n",
    "\n",
    "class ScopeHeatmapGenerator(ast.NodeVisitor):\n",
    "    def __init__(self, code):\n",
    "        self.code = code\n",
    "        self.scopes = []  # A list of sets, where each set represents variables in a scope\n",
    "        self.active_vars_per_line = [0] * (len(code.split('\\n')) + 1)  # Initialize the count of active variables per line\n",
    "\n",
    "    def enter_scope(self):\n",
    "        if not self.scopes:\n",
    "            self.scopes.append(set())  # Create the first global scope\n",
    "        else:\n",
    "            # New scope inherits all variables from the previous scope\n",
    "            self.scopes.append(self.scopes[-1].copy())\n",
    "\n",
    "    def exit_scope(self):\n",
    "        self.scopes.pop()  # Remove the top scope when exiting\n",
    "\n",
    "    def visit_FunctionDef(self, node):\n",
    "        self.enter_scope()\n",
    "        for arg in node.args.args:\n",
    "            self.scopes[-1].add(arg.arg)\n",
    "        self.generic_visit(node)\n",
    "        self.exit_scope()\n",
    "\n",
    "    def visit_AsyncFunctionDef(self, node):\n",
    "        self.enter_scope()\n",
    "        for arg in node.args.args:\n",
    "            self.scopes[-1].add(arg.arg)\n",
    "        self.generic_visit(node)\n",
    "        self.exit_scope()\n",
    "\n",
    "    def visit_For(self, node):\n",
    "        self.enter_scope()\n",
    "        if isinstance(node.target, ast.Name):\n",
    "            self.scopes[-1].add(node.target.id)\n",
    "        self.generic_visit(node)\n",
    "        self.exit_scope()\n",
    "\n",
    "    def visit_Assign(self, node):\n",
    "        for target in node.targets:\n",
    "            if isinstance(target, ast.Name):\n",
    "                self.scopes[-1].add(target.id)\n",
    "        self.generic_visit(node)\n",
    "\n",
    "    def generic_visit(self, node):\n",
    "        \"\"\"Called if no explicit visitor function exists for a node.\"\"\"\n",
    "        if hasattr(node, 'lineno'):\n",
    "            # Count the number of variables in scope at this line\n",
    "            self.active_vars_per_line[node.lineno] = len(self.scopes[-1])\n",
    "        super().generic_visit(node)\n",
    "\n",
    "    def analyze(self):\n",
    "        \"\"\"Analyze the given code to generate the scope heatmap.\"\"\"\n",
    "        # Parse the code into an AST\n",
    "        tree = ast.parse(self.code)\n",
    "        # Start with the global scope\n",
    "        self.enter_scope()\n",
    "        # Visit all nodes\n",
    "        self.visit(tree)\n",
    "        # Exit the global scope\n",
    "        self.exit_scope()\n",
    "        \n",
    "        # Normalize the heatmap data to remove any lines beyond the code length\n",
    "        code_length = len(self.code.split('\\n'))\n",
    "        self.active_vars_per_line = self.active_vars_per_line[:code_length + 1]\n",
    "\n",
    "        return self.active_vars_per_line\n",
    "\n",
    "# Initialize the heatmap generator with the sample code\n",
    "heatmap_generator = ScopeHeatmapGenerator(sample_code)\n",
    "\n",
    "# Generate the heatmap\n",
    "heatmap_data = heatmap_generator.analyze()\n",
    "heatmap_data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " 1:                                                                                  | \n",
      " 2: def e2e_eval(gt_dir, res_dir, ignore_blank=False):                               | \n",
      " 3:     print('start testing...')                                                    | █\n",
      " 4:     iou_thresh = 0.5                                                             | █\n",
      " 5:     val_names = os.listdir(gt_dir)                                               | █\n",
      " 6:     num_gt_chars = 0                                                             | ██\n",
      " 7:     gt_count = 0                                                                 | ██\n",
      " 8:     dt_count = 0                                                                 | ██\n",
      " 9:     hit = 0                                                                      | ███\n",
      "10:     ed_sum = 0                                                                   | ███\n",
      "11:     for i, val_name in enumerate(val_names):                                     | ████\n",
      "12:         with open(os.path.join(gt_dir, val_name), encoding='utf-8') as f:        | ████\n",
      "13:             gt_lines = [o.strip() for o in f.readlines()]                        | ████\n",
      "14:         gts = []                                                                 | ████\n",
      "15:         ignore_masks = []                                                        | █████\n",
      "16:         for line in gt_lines:                                                    | █████\n",
      "17:             parts = line.strip().split('\t')                                      | █████\n",
      "18:             # ignore illegal data                                                | ██████\n",
      "19:             if len(parts) < 9:                                                   | \n",
      "20:                 continue                                                         | ██████\n",
      "21:             assert (len(parts) < 11)                                             | ██████\n",
      "22:             if len(parts) == 9:                                                  | ██████\n",
      "23:                 gts.append(parts[:8] + [''])                                     | ██████\n",
      "24:             else:                                                                | ██████\n",
      "25:                 gts.append(parts[:8] + [parts[-1]])                              | \n",
      "26:             ignore_masks.append(parts[8])                                        | ██████\n",
      "27:         val_path = os.path.join(res_dir, val_name)                               | ██████\n",
      "28:         if not os.path.exists(val_path):                                         | █████\n",
      "29:             dt_lines = []                                                        | █████\n",
      "30:         else:                                                                    | ██████\n",
      "31:             with open(val_path, encoding='utf-8') as f:                          | \n",
      "32:                 dt_lines = [o.strip() for o in f.readlines()]                    | ██████\n",
      "33:         dts = []                                                                 | ██████\n",
      "34:         for line in dt_lines:                                                    | ██████\n",
      "35:             # print(line)                                                        | ███████\n",
      "36:             parts = line.strip().split(\"\t\")                                      | \n",
      "37:             assert (len(parts) < 10), \"line error: {}\".format(line)              | ███████\n",
      "38:             if len(parts) == 8:                                                  | ███████\n",
      "39:                 dts.append(parts + [''])                                         | ███████\n",
      "40:             else:                                                                | ███████\n",
      "41:                 dts.append(parts)                                                | \n",
      "42:         dt_match = [False] * len(dts)                                            | ███████\n",
      "43:         gt_match = [False] * len(gts)                                            | ███████\n",
      "44:         all_ious = defaultdict(tuple)                                            | ███████\n",
      "45:         for index_gt, gt in enumerate(gts):                                      | ███████\n",
      "46:             gt_coors = [float(gt_coor) for gt_coor in gt[0:8]]                   | ███████\n",
      "47:             gt_poly = polygon_from_str(gt_coors)                                 | ████████\n",
      "48:             for index_dt, dt in enumerate(dts):                                  | ████████\n",
      "49:                 dt_coors = [float(dt_coor) for dt_coor in dt[0:8]]               | ████████\n",
      "50:                 dt_poly = polygon_from_str(dt_coors)                             | █████████\n",
      "51:                 iou = polygon_iou(dt_poly, gt_poly)                              | █████████\n",
      "52:                 if iou >= iou_thresh:                                            | ██████████\n",
      "53:                     all_ious[(index_gt, index_dt)] = iou                         | ██████████\n",
      "54:         sorted_ious = sorted(                                                    | ██████████\n",
      "55:             all_ious.items(), key=operator.itemgetter(1), reverse=True)          | ████████\n",
      "56:         sorted_gt_dt_pairs = [item[0] for item in sorted_ious]                   | ████████\n",
      "57:                                                                                  | ████████\n",
      "58:         # matched gt and dt                                                      | \n",
      "59:         for gt_dt_pair in sorted_gt_dt_pairs:                                    | \n",
      "60:             index_gt, index_dt = gt_dt_pair                                      | █████████\n",
      "61:             if gt_match[index_gt] == False and dt_match[index_dt] == False:      | █████████\n",
      "62:                 gt_match[index_gt] = True                                        | █████████\n",
      "63:                 dt_match[index_dt] = True                                        | █████████\n",
      "64:                 if ignore_blank:                                                 | █████████\n",
      "65:                     gt_str = strQ2B(gts[index_gt][8]).replace(\" \", \"\")           | █████████\n",
      "66:                     dt_str = strQ2B(dts[index_dt][8]).replace(\" \", \"\")           | █████████\n",
      "67:                 else:                                                            | ██████████\n",
      "68:                     gt_str = strQ2B(gts[index_gt][8])                            | \n",
      "69:                     dt_str = strQ2B(dts[index_dt][8])                            | ██████████\n",
      "70:                 if ignore_masks[index_gt] == '0':                                | ██████████\n",
      "71:                     ed_sum += ed(gt_str, dt_str)                                 | ██████████\n",
      "72:                     num_gt_chars += len(gt_str)                                  | ██████████\n",
      "73:                     if gt_str == dt_str:                                         | ██████████\n",
      "74:                         hit += 1                                                 | ██████████\n",
      "75:                     gt_count += 1                                                | ██████████\n",
      "76:                     dt_count += 1                                                | ██████████\n",
      "77:                                                                                  | ██████████\n",
      "78:         # unmatched dt                                                           | \n",
      "79:         for tindex, dt_match_flag in enumerate(dt_match):                        | \n",
      "80:             if dt_match_flag == False:                                           | ████████\n",
      "81:                 dt_str = dts[tindex][8]                                          | ████████\n",
      "82:                 gt_str = ''                                                      | █████████\n",
      "83:                 ed_sum += ed(dt_str, gt_str)                                     | █████████\n",
      "84:                 dt_count += 1                                                    | █████████\n",
      "85:                                                                                  | █████████\n",
      "86:         # unmatched gt                                                           | \n",
      "87:         for tindex, gt_match_flag in enumerate(gt_match):                        | \n",
      "88:             if gt_match_flag == False and ignore_masks[tindex] == '0':           | ████████\n",
      "89:                 dt_str = ''                                                      | ████████\n",
      "90:                 gt_str = gts[tindex][8]                                          | █████████\n",
      "91:                 ed_sum += ed(gt_str, dt_str)                                     | █████████\n",
      "92:                 num_gt_chars += len(gt_str)                                      | █████████\n",
      "93:                 gt_count += 1                                                    | █████████\n",
      "94:                                                                                  | █████████\n",
      "95:     eps = 1e-9                                                                   | \n",
      "96:     print('hit, dt_count, gt_count', hit, dt_count, gt_count)                    | ████\n",
      "97:     precision = hit / (dt_count + eps)                                           | ████\n",
      "98:     recall = hit / (gt_count + eps)                                              | █████\n",
      "99:     fmeasure = 2.0 * precision * recall / (precision + recall + eps)             | █████\n",
      "100:     avg_edit_dist_img = ed_sum / len(val_names)                                  | █████\n",
      "101:     avg_edit_dist_field = ed_sum / (gt_count + eps)                              | ██████\n",
      "102:     character_acc = 1 - ed_sum / (num_gt_chars + eps)                            | ██████\n",
      "103:     print('character_acc: %.2f' % (character_acc * 100) + \"%\")                   | ███████\n",
      "104:     print('avg_edit_dist_field: %.2f' % (avg_edit_dist_field))                   | ███████\n",
      "105:     print('avg_edit_dist_img: %.2f' % (avg_edit_dist_img))                       | ███████\n",
      "106:     print('precision: %.2f' % (precision * 100) + \"%\")                           | ███████\n",
      "107:     print('recall: %.2f' % (recall * 100) + \"%\")                                 | ███████\n",
      "108:     print('fmeasure: %.2f' % (fmeasure * 100) + \"%\")                             | ███████\n",
      "109:                                                                                  | ███████\n"
     ]
    }
   ],
   "source": [
    "def visualize_heatmap(code, heatmap_data):\n",
    "    lines = code.split('\\n')\n",
    "    max_heat = max(heatmap_data)\n",
    "    # Normalize heat levels to a scale of 0-10\n",
    "    normalized_heatmap = [int((heat / max_heat) * 10) if max_heat > 0 else 0 for heat in heatmap_data]\n",
    "\n",
    "    # Generate visualization for each line\n",
    "    for i, line in enumerate(lines, start=1):\n",
    "        heat_bar = '█' * normalized_heatmap[i - 1]\n",
    "        print(f\"{str(i).rjust(2)}: {line[:80].ljust(80)} | {heat_bar}\")\n",
    "\n",
    "# Visualize the heatmap alongside the code\n",
    "visualize_heatmap(sample_code, heatmap_data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'print': {'first_used_line': 3,\n",
       "  'last_used_line': 108,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'iou_thresh': {'first_used_line': 4,\n",
       "  'last_used_line': 52,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'val_names': {'first_used_line': 5,\n",
       "  'last_used_line': 100,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'os': {'first_used_line': 5,\n",
       "  'last_used_line': 28,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'gt_dir': {'first_used_line': 5,\n",
       "  'last_used_line': 12,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'num_gt_chars': {'first_used_line': 6,\n",
       "  'last_used_line': 102,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'gt_count': {'first_used_line': 7,\n",
       "  'last_used_line': 101,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'dt_count': {'first_used_line': 8,\n",
       "  'last_used_line': 97,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'hit': {'first_used_line': 9,\n",
       "  'last_used_line': 98,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'ed_sum': {'first_used_line': 10,\n",
       "  'last_used_line': 102,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'i': {'first_used_line': 11,\n",
       "  'last_used_line': 11,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'val_name': {'first_used_line': 11,\n",
       "  'last_used_line': 27,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'enumerate': {'first_used_line': 11,\n",
       "  'last_used_line': 87,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'open': {'first_used_line': 12,\n",
       "  'last_used_line': 31,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'f': {'first_used_line': 12,\n",
       "  'last_used_line': 32,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'gt_lines': {'first_used_line': 13,\n",
       "  'last_used_line': 16,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'o': {'first_used_line': 13,\n",
       "  'last_used_line': 32,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'gts': {'first_used_line': 14,\n",
       "  'last_used_line': 90,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'ignore_masks': {'first_used_line': 15,\n",
       "  'last_used_line': 88,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'line': {'first_used_line': 16,\n",
       "  'last_used_line': 37,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'parts': {'first_used_line': 17,\n",
       "  'last_used_line': 41,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'len': {'first_used_line': 19,\n",
       "  'last_used_line': 100,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'val_path': {'first_used_line': 27,\n",
       "  'last_used_line': 31,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'res_dir': {'first_used_line': 27,\n",
       "  'last_used_line': 27,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'dt_lines': {'first_used_line': 29,\n",
       "  'last_used_line': 34,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'dts': {'first_used_line': 33,\n",
       "  'last_used_line': 81,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'dt_match': {'first_used_line': 42,\n",
       "  'last_used_line': 79,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'gt_match': {'first_used_line': 43,\n",
       "  'last_used_line': 87,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'all_ious': {'first_used_line': 44,\n",
       "  'last_used_line': 55,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'defaultdict': {'first_used_line': 44,\n",
       "  'last_used_line': 44,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'tuple': {'first_used_line': 44,\n",
       "  'last_used_line': 44,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'index_gt': {'first_used_line': 45,\n",
       "  'last_used_line': 70,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'gt': {'first_used_line': 45,\n",
       "  'last_used_line': 46,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'gt_coors': {'first_used_line': 46,\n",
       "  'last_used_line': 47,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'float': {'first_used_line': 46,\n",
       "  'last_used_line': 49,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'gt_coor': {'first_used_line': 46,\n",
       "  'last_used_line': 46,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'gt_poly': {'first_used_line': 47,\n",
       "  'last_used_line': 51,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'polygon_from_str': {'first_used_line': 47,\n",
       "  'last_used_line': 50,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'index_dt': {'first_used_line': 48,\n",
       "  'last_used_line': 69,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'dt': {'first_used_line': 48,\n",
       "  'last_used_line': 49,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'dt_coors': {'first_used_line': 49,\n",
       "  'last_used_line': 50,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'dt_coor': {'first_used_line': 49,\n",
       "  'last_used_line': 49,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'dt_poly': {'first_used_line': 50,\n",
       "  'last_used_line': 51,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'iou': {'first_used_line': 51,\n",
       "  'last_used_line': 53,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'polygon_iou': {'first_used_line': 51,\n",
       "  'last_used_line': 51,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'sorted_ious': {'first_used_line': 54,\n",
       "  'last_used_line': 56,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'sorted': {'first_used_line': 54,\n",
       "  'last_used_line': 54,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'operator': {'first_used_line': 55,\n",
       "  'last_used_line': 55,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'sorted_gt_dt_pairs': {'first_used_line': 56,\n",
       "  'last_used_line': 59,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'item': {'first_used_line': 56,\n",
       "  'last_used_line': 56,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'gt_dt_pair': {'first_used_line': 59,\n",
       "  'last_used_line': 60,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'ignore_blank': {'first_used_line': 64,\n",
       "  'last_used_line': 64,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'gt_str': {'first_used_line': 65,\n",
       "  'last_used_line': 92,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'strQ2B': {'first_used_line': 65,\n",
       "  'last_used_line': 69,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'dt_str': {'first_used_line': 66,\n",
       "  'last_used_line': 91,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'ed': {'first_used_line': 71,\n",
       "  'last_used_line': 91,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'tindex': {'first_used_line': 79,\n",
       "  'last_used_line': 90,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'dt_match_flag': {'first_used_line': 79,\n",
       "  'last_used_line': 80,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'gt_match_flag': {'first_used_line': 87,\n",
       "  'last_used_line': 88,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'eps': {'first_used_line': 95,\n",
       "  'last_used_line': 102,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'precision': {'first_used_line': 97,\n",
       "  'last_used_line': 106,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'recall': {'first_used_line': 98,\n",
       "  'last_used_line': 107,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'fmeasure': {'first_used_line': 99,\n",
       "  'last_used_line': 108,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'avg_edit_dist_img': {'first_used_line': 100,\n",
       "  'last_used_line': 105,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'avg_edit_dist_field': {'first_used_line': 101,\n",
       "  'last_used_line': 104,\n",
       "  'parent_function': 'e2e_eval'},\n",
       " 'character_acc': {'first_used_line': 102,\n",
       "  'last_used_line': 103,\n",
       "  'parent_function': 'e2e_eval'}}"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import ast\n",
    "\n",
    "# Parse the Python code into an AST\n",
    "parsed_code = ast.parse(sample_code)\n",
    "\n",
    "# Helper function to traverse the AST and find the spans\n",
    "def find_usage_spans(node, usage_dict=None, parent_function=None):\n",
    "    if usage_dict is None:\n",
    "        usage_dict = {}\n",
    "    \n",
    "    for child in ast.iter_child_nodes(node):\n",
    "        if isinstance(child, ast.Name):\n",
    "            name = child.id\n",
    "            if name not in usage_dict:\n",
    "                usage_dict[name] = {'first_used_line': child.lineno, 'last_used_line': child.lineno, 'parent_function': parent_function}\n",
    "            else:\n",
    "                usage_dict[name]['last_used_line'] = child.lineno\n",
    "        \n",
    "        elif isinstance(child, ast.FunctionDef):\n",
    "            parent_function = child.name\n",
    "        \n",
    "        find_usage_spans(child, usage_dict, parent_function)\n",
    "    \n",
    "    return usage_dict\n",
    "\n",
    "# Finding the usage spans\n",
    "usage_spans = find_usage_spans(parsed_code)\n",
    "\n",
    "usage_spans"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'line 3': 1,\n",
       " 'line 4': 2,\n",
       " 'line 5': 5,\n",
       " 'line 6': 6,\n",
       " 'line 7': 7,\n",
       " 'line 8': 8,\n",
       " 'line 9': 9,\n",
       " 'line 10': 10,\n",
       " 'line 11': 13,\n",
       " 'line 12': 14,\n",
       " 'line 13': 15,\n",
       " 'line 14': 16,\n",
       " 'line 15': 17,\n",
       " 'line 16': 18,\n",
       " 'line 17': 18,\n",
       " 'line 18': 18,\n",
       " 'line 19': 19,\n",
       " 'line 20': 19,\n",
       " 'line 21': 19,\n",
       " 'line 22': 19,\n",
       " 'line 23': 19,\n",
       " 'line 24': 19,\n",
       " 'line 25': 19,\n",
       " 'line 26': 19,\n",
       " 'line 27': 21,\n",
       " 'line 28': 19,\n",
       " 'line 29': 19,\n",
       " 'line 30': 19,\n",
       " 'line 31': 19,\n",
       " 'line 32': 17,\n",
       " 'line 33': 16,\n",
       " 'line 34': 16,\n",
       " 'line 35': 15,\n",
       " 'line 36': 15,\n",
       " 'line 37': 15,\n",
       " 'line 38': 14,\n",
       " 'line 39': 14,\n",
       " 'line 40': 14,\n",
       " 'line 41': 14,\n",
       " 'line 42': 14,\n",
       " 'line 43': 15,\n",
       " 'line 44': 18,\n",
       " 'line 45': 18,\n",
       " 'line 46': 21,\n",
       " 'line 47': 21,\n",
       " 'line 48': 22,\n",
       " 'line 49': 24,\n",
       " 'line 50': 22,\n",
       " 'line 51': 22,\n",
       " 'line 52': 19,\n",
       " 'line 53': 18,\n",
       " 'line 54': 19,\n",
       " 'line 55': 19,\n",
       " 'line 56': 19,\n",
       " 'line 57': 17,\n",
       " 'line 58': 17,\n",
       " 'line 59': 18,\n",
       " 'line 60': 17,\n",
       " 'line 61': 16,\n",
       " 'line 62': 16,\n",
       " 'line 63': 16,\n",
       " 'line 64': 17,\n",
       " 'line 65': 18,\n",
       " 'line 66': 19,\n",
       " 'line 67': 19,\n",
       " 'line 68': 19,\n",
       " 'line 69': 19,\n",
       " 'line 70': 17,\n",
       " 'line 71': 17,\n",
       " 'line 72': 17,\n",
       " 'line 73': 17,\n",
       " 'line 74': 17,\n",
       " 'line 75': 17,\n",
       " 'line 76': 17,\n",
       " 'line 77': 17,\n",
       " 'line 78': 17,\n",
       " 'line 79': 19,\n",
       " 'line 80': 18,\n",
       " 'line 81': 17,\n",
       " 'line 82': 16,\n",
       " 'line 83': 16,\n",
       " 'line 84': 16,\n",
       " 'line 85': 16,\n",
       " 'line 86': 16,\n",
       " 'line 87': 17,\n",
       " 'line 88': 15,\n",
       " 'line 89': 13,\n",
       " 'line 90': 13,\n",
       " 'line 91': 11,\n",
       " 'line 92': 9,\n",
       " 'line 93': 8,\n",
       " 'line 94': 8,\n",
       " 'line 95': 9,\n",
       " 'line 96': 9,\n",
       " 'line 97': 10,\n",
       " 'line 98': 10,\n",
       " 'line 99': 10,\n",
       " 'line 100': 11,\n",
       " 'line 101': 10,\n",
       " 'line 102': 10,\n",
       " 'line 103': 7,\n",
       " 'line 104': 6,\n",
       " 'line 105': 5,\n",
       " 'line 106': 4,\n",
       " 'line 107': 3,\n",
       " 'line 108': 2}"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Function to determine the number of active variables at each line\n",
    "def active_variables_per_line(usage_spans):\n",
    "    # Initialize a dictionary to count active variables per line\n",
    "    active_vars = {}\n",
    "\n",
    "    # Iterate through each variable and its span\n",
    "    for var, spans in usage_spans.items():\n",
    "        # If the variable is within a function, we'll only count it within that function's scope\n",
    "        # for line in (spans['first_used_line'], spans['last_used_line'] + 1):\n",
    "        for line in range(spans['first_used_line'], spans['last_used_line'] + 1):\n",
    "        # for line in (spans['last_used_line'] + 1,):\n",
    "            key = line\n",
    "            if key in active_vars:\n",
    "                active_vars[key].add(var)\n",
    "            else:\n",
    "                active_vars[key] = {var}\n",
    "    \n",
    "    # Convert sets to counts\n",
    "    active_vars_counts = {key: len(vars) for key, vars in active_vars.items()}\n",
    "    \n",
    "    return active_vars_counts\n",
    "\n",
    "# Determine the number of active variables for each line number\n",
    "active_vars_counts = active_variables_per_line(usage_spans)\n",
    "\n",
    "# Since the function above creates a tuple (function_name, line) as key when in a function scope,\n",
    "# we'll create a more readable format by concatenating the function name and line number.\n",
    "formatted_active_vars_counts = {\n",
    "    f\"line {key}\": count\n",
    "    for key, count in active_vars_counts.items()\n",
    "}\n",
    "\n",
    "formatted_active_vars_counts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "pre { line-height: 125%; }\n",
       "td.linenos .normal { color: #666666; background-color: transparent; padding-left: 5px; padding-right: 5px; }\n",
       "span.linenos { color: #666666; background-color: transparent; padding-left: 5px; padding-right: 5px; }\n",
       "td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }\n",
       "span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }\n",
       ".hll { background-color: #ffffcc }\n",
       ".c { color: #60a0b0; font-style: italic } /* Comment */\n",
       ".err { border: 1px solid #FF0000 } /* Error */\n",
       ".k { color: #007020; font-weight: bold } /* Keyword */\n",
       ".o { color: #666666 } /* Operator */\n",
       ".ch { color: #60a0b0; font-style: italic } /* Comment.Hashbang */\n",
       ".cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */\n",
       ".cp { color: #007020 } /* Comment.Preproc */\n",
       ".cpf { color: #60a0b0; font-style: italic } /* Comment.PreprocFile */\n",
       ".c1 { color: #60a0b0; font-style: italic } /* Comment.Single */\n",
       ".cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */\n",
       ".gd { color: #A00000 } /* Generic.Deleted */\n",
       ".ge { font-style: italic } /* Generic.Emph */\n",
       ".ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */\n",
       ".gr { color: #FF0000 } /* Generic.Error */\n",
       ".gh { color: #000080; font-weight: bold } /* Generic.Heading */\n",
       ".gi { color: #00A000 } /* Generic.Inserted */\n",
       ".go { color: #888888 } /* Generic.Output */\n",
       ".gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */\n",
       ".gs { font-weight: bold } /* Generic.Strong */\n",
       ".gu { color: #800080; font-weight: bold } /* Generic.Subheading */\n",
       ".gt { color: #0044DD } /* Generic.Traceback */\n",
       ".kc { color: #007020; font-weight: bold } /* Keyword.Constant */\n",
       ".kd { color: #007020; font-weight: bold } /* Keyword.Declaration */\n",
       ".kn { color: #007020; font-weight: bold } /* Keyword.Namespace */\n",
       ".kp { color: #007020 } /* Keyword.Pseudo */\n",
       ".kr { color: #007020; font-weight: bold } /* Keyword.Reserved */\n",
       ".kt { color: #902000 } /* Keyword.Type */\n",
       ".m { color: #40a070 } /* Literal.Number */\n",
       ".s { color: #4070a0 } /* Literal.String */\n",
       ".na { color: #4070a0 } /* Name.Attribute */\n",
       ".nb { color: #007020 } /* Name.Builtin */\n",
       ".nc { color: #0e84b5; font-weight: bold } /* Name.Class */\n",
       ".no { color: #60add5 } /* Name.Constant */\n",
       ".nd { color: #555555; font-weight: bold } /* Name.Decorator */\n",
       ".ni { color: #d55537; font-weight: bold } /* Name.Entity */\n",
       ".ne { color: #007020 } /* Name.Exception */\n",
       ".nf { color: #06287e } /* Name.Function */\n",
       ".nl { color: #002070; font-weight: bold } /* Name.Label */\n",
       ".nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */\n",
       ".nt { color: #062873; font-weight: bold } /* Name.Tag */\n",
       ".nv { color: #bb60d5 } /* Name.Variable */\n",
       ".ow { color: #007020; font-weight: bold } /* Operator.Word */\n",
       ".w { color: #bbbbbb } /* Text.Whitespace */\n",
       ".mb { color: #40a070 } /* Literal.Number.Bin */\n",
       ".mf { color: #40a070 } /* Literal.Number.Float */\n",
       ".mh { color: #40a070 } /* Literal.Number.Hex */\n",
       ".mi { color: #40a070 } /* Literal.Number.Integer */\n",
       ".mo { color: #40a070 } /* Literal.Number.Oct */\n",
       ".sa { color: #4070a0 } /* Literal.String.Affix */\n",
       ".sb { color: #4070a0 } /* Literal.String.Backtick */\n",
       ".sc { color: #4070a0 } /* Literal.String.Char */\n",
       ".dl { color: #4070a0 } /* Literal.String.Delimiter */\n",
       ".sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */\n",
       ".s2 { color: #4070a0 } /* Literal.String.Double */\n",
       ".se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */\n",
       ".sh { color: #4070a0 } /* Literal.String.Heredoc */\n",
       ".si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */\n",
       ".sx { color: #c65d09 } /* Literal.String.Other */\n",
       ".sr { color: #235388 } /* Literal.String.Regex */\n",
       ".s1 { color: #4070a0 } /* Literal.String.Single */\n",
       ".ss { color: #517918 } /* Literal.String.Symbol */\n",
       ".bp { color: #007020 } /* Name.Builtin.Pseudo */\n",
       ".fm { color: #06287e } /* Name.Function.Magic */\n",
       ".vc { color: #bb60d5 } /* Name.Variable.Class */\n",
       ".vg { color: #bb60d5 } /* Name.Variable.Global */\n",
       ".vi { color: #bb60d5 } /* Name.Variable.Instance */\n",
       ".vm { color: #bb60d5 } /* Name.Variable.Magic */\n",
       ".il { color: #40a070 } /* Literal.Number.Integer.Long */<pre style=\"line-height: 125%\"><span style=\"background-color: rgb(255,255,255)\">\n",
       "</span><span style=\"background-color: rgb(255,255,255)\">def e2e_eval(gt_dir, res_dir, ignore_blank=False):\n",
       "</span><span style=\"background-color: rgb(255,247,247)\">    print('start testing...')\n",
       "</span><span style=\"background-color: rgb(255,239,239)\">    iou_thresh = 0.5\n",
       "</span><span style=\"background-color: rgb(255,214,214)\">    val_names = os.listdir(gt_dir)\n",
       "</span><span style=\"background-color: rgb(255,205,205)\">    num_gt_chars = 0\n",
       "</span><span style=\"background-color: rgb(255,197,197)\">    gt_count = 0\n",
       "</span><span style=\"background-color: rgb(255,189,189)\">    dt_count = 0\n",
       "</span><span style=\"background-color: rgb(255,180,180)\">    hit = 0\n",
       "</span><span style=\"background-color: rgb(255,172,172)\">    ed_sum = 0\n",
       "</span><span style=\"background-color: rgb(255,147,147)\">    for i, val_name in enumerate(val_names):\n",
       "</span><span style=\"background-color: rgb(255,139,139)\">        with open(os.path.join(gt_dir, val_name), encoding='utf-8') as f:\n",
       "</span><span style=\"background-color: rgb(255,130,130)\">            gt_lines = [o.strip() for o in f.readlines()]\n",
       "</span><span style=\"background-color: rgb(255,122,122)\">        gts = []\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">        ignore_masks = []\n",
       "</span><span style=\"background-color: rgb(255,105,105)\">        for line in gt_lines:\n",
       "</span><span style=\"background-color: rgb(255,105,105)\">            parts = line.strip().split('\\t')\n",
       "</span><span style=\"background-color: rgb(255,105,105)\">            # ignore illegal data\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">            if len(parts) < 9:\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">                continue\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">            assert (len(parts) < 11)\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">            if len(parts) == 9:\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">                gts.append(parts[:8] + [''])\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">            else:\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">                gts.append(parts[:8] + [parts[-1]])\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">            ignore_masks.append(parts[8])\n",
       "</span><span style=\"background-color: rgb(255,80,80)\">        val_path = os.path.join(res_dir, val_name)\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">        if not os.path.exists(val_path):\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">            dt_lines = []\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">        else:\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">            with open(val_path, encoding='utf-8') as f:\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">                dt_lines = [o.strip() for o in f.readlines()]\n",
       "</span><span style=\"background-color: rgb(255,122,122)\">        dts = []\n",
       "</span><span style=\"background-color: rgb(255,122,122)\">        for line in dt_lines:\n",
       "</span><span style=\"background-color: rgb(255,130,130)\">            # print(line)\n",
       "</span><span style=\"background-color: rgb(255,130,130)\">            parts = line.strip().split(\"\\t\")\n",
       "</span><span style=\"background-color: rgb(255,130,130)\">            assert (len(parts) < 10), \"line error: {}\".format(line)\n",
       "</span><span style=\"background-color: rgb(255,139,139)\">            if len(parts) == 8:\n",
       "</span><span style=\"background-color: rgb(255,139,139)\">                dts.append(parts + [''])\n",
       "</span><span style=\"background-color: rgb(255,139,139)\">            else:\n",
       "</span><span style=\"background-color: rgb(255,139,139)\">                dts.append(parts)\n",
       "</span><span style=\"background-color: rgb(255,139,139)\">        dt_match = [False] * len(dts)\n",
       "</span><span style=\"background-color: rgb(255,130,130)\">        gt_match = [False] * len(gts)\n",
       "</span><span style=\"background-color: rgb(255,105,105)\">        all_ious = defaultdict(tuple)\n",
       "</span><span style=\"background-color: rgb(255,105,105)\">        for index_gt, gt in enumerate(gts):\n",
       "</span><span style=\"background-color: rgb(255,80,80)\">            gt_coors = [float(gt_coor) for gt_coor in gt[0:8]]\n",
       "</span><span style=\"background-color: rgb(255,80,80)\">            gt_poly = polygon_from_str(gt_coors)\n",
       "</span><span style=\"background-color: rgb(255,72,72)\">            for index_dt, dt in enumerate(dts):\n",
       "</span><span style=\"background-color: rgb(255,55,55)\">                dt_coors = [float(dt_coor) for dt_coor in dt[0:8]]\n",
       "</span><span style=\"background-color: rgb(255,72,72)\">                dt_poly = polygon_from_str(dt_coors)\n",
       "</span><span style=\"background-color: rgb(255,72,72)\">                iou = polygon_iou(dt_poly, gt_poly)\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">                if iou >= iou_thresh:\n",
       "</span><span style=\"background-color: rgb(255,105,105)\">                    all_ious[(index_gt, index_dt)] = iou\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">        sorted_ious = sorted(\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">            all_ious.items(), key=operator.itemgetter(1), reverse=True)\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">        sorted_gt_dt_pairs = [item[0] for item in sorted_ious]\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">        # matched gt and dt\n",
       "</span><span style=\"background-color: rgb(255,105,105)\">        for gt_dt_pair in sorted_gt_dt_pairs:\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">            index_gt, index_dt = gt_dt_pair\n",
       "</span><span style=\"background-color: rgb(255,122,122)\">            if gt_match[index_gt] == False and dt_match[index_dt] == False:\n",
       "</span><span style=\"background-color: rgb(255,122,122)\">                gt_match[index_gt] = True\n",
       "</span><span style=\"background-color: rgb(255,122,122)\">                dt_match[index_dt] = True\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">                if ignore_blank:\n",
       "</span><span style=\"background-color: rgb(255,105,105)\">                    gt_str = strQ2B(gts[index_gt][8]).replace(\" \", \"\")\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">                    dt_str = strQ2B(dts[index_dt][8]).replace(\" \", \"\")\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">                else:\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">                    gt_str = strQ2B(gts[index_gt][8])\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">                    dt_str = strQ2B(dts[index_dt][8])\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">                if ignore_masks[index_gt] == '0':\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">                    ed_sum += ed(gt_str, dt_str)\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">                    num_gt_chars += len(gt_str)\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">                    if gt_str == dt_str:\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">                        hit += 1\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">                    gt_count += 1\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">                    dt_count += 1\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">        # unmatched dt\n",
       "</span><span style=\"background-color: rgb(255,97,97)\">        for tindex, dt_match_flag in enumerate(dt_match):\n",
       "</span><span style=\"background-color: rgb(255,105,105)\">            if dt_match_flag == False:\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">                dt_str = dts[tindex][8]\n",
       "</span><span style=\"background-color: rgb(255,122,122)\">                gt_str = ''\n",
       "</span><span style=\"background-color: rgb(255,122,122)\">                ed_sum += ed(dt_str, gt_str)\n",
       "</span><span style=\"background-color: rgb(255,122,122)\">                dt_count += 1\n",
       "</span><span style=\"background-color: rgb(255,122,122)\">\n",
       "</span><span style=\"background-color: rgb(255,122,122)\">        # unmatched gt\n",
       "</span><span style=\"background-color: rgb(255,114,114)\">        for tindex, gt_match_flag in enumerate(gt_match):\n",
       "</span><span style=\"background-color: rgb(255,130,130)\">            if gt_match_flag == False and ignore_masks[tindex] == '0':\n",
       "</span><span style=\"background-color: rgb(255,147,147)\">                dt_str = ''\n",
       "</span><span style=\"background-color: rgb(255,147,147)\">                gt_str = gts[tindex][8]\n",
       "</span><span style=\"background-color: rgb(255,164,164)\">                ed_sum += ed(gt_str, dt_str)\n",
       "</span><span style=\"background-color: rgb(255,180,180)\">                num_gt_chars += len(gt_str)\n",
       "</span><span style=\"background-color: rgb(255,189,189)\">                gt_count += 1\n",
       "</span><span style=\"background-color: rgb(255,189,189)\">\n",
       "</span><span style=\"background-color: rgb(255,180,180)\">    eps = 1e-9\n",
       "</span><span style=\"background-color: rgb(255,180,180)\">    print('hit, dt_count, gt_count', hit, dt_count, gt_count)\n",
       "</span><span style=\"background-color: rgb(255,172,172)\">    precision = hit / (dt_count + eps)\n",
       "</span><span style=\"background-color: rgb(255,172,172)\">    recall = hit / (gt_count + eps)\n",
       "</span><span style=\"background-color: rgb(255,172,172)\">    fmeasure = 2.0 * precision * recall / (precision + recall + eps)\n",
       "</span><span style=\"background-color: rgb(255,164,164)\">    avg_edit_dist_img = ed_sum / len(val_names)\n",
       "</span><span style=\"background-color: rgb(255,172,172)\">    avg_edit_dist_field = ed_sum / (gt_count + eps)\n",
       "</span><span style=\"background-color: rgb(255,172,172)\">    character_acc = 1 - ed_sum / (num_gt_chars + eps)\n",
       "</span><span style=\"background-color: rgb(255,197,197)\">    print('character_acc: %.2f' % (character_acc * 100) + \"%\")\n",
       "</span><span style=\"background-color: rgb(255,205,205)\">    print('avg_edit_dist_field: %.2f' % (avg_edit_dist_field))\n",
       "</span><span style=\"background-color: rgb(255,214,214)\">    print('avg_edit_dist_img: %.2f' % (avg_edit_dist_img))\n",
       "</span><span style=\"background-color: rgb(255,222,222)\">    print('precision: %.2f' % (precision * 100) + \"%\")\n",
       "</span><span style=\"background-color: rgb(255,230,230)\">    print('recall: %.2f' % (recall * 100) + \"%\")\n",
       "</span><span style=\"background-color: rgb(255,239,239)\">    print('fmeasure: %.2f' % (fmeasure * 100) + \"%\")\n",
       "</span></pre>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from pygments import highlight\n",
    "from pygments.lexers import PythonLexer\n",
    "from pygments.formatters import HtmlFormatter\n",
    "from pygments.styles import get_style_by_name\n",
    "import IPython\n",
    "\n",
    "# Function to generate HTML representation of the code with heatmap colors\n",
    "def generate_heatmap_html(code, active_vars_counts):\n",
    "    # Define the style for syntax highlighting\n",
    "    style = get_style_by_name('friendly')\n",
    "    formatter = HtmlFormatter(style=style)\n",
    "    # Get the tokenized code in HTML\n",
    "    highlighted_code = highlight(code, PythonLexer(), formatter)\n",
    "    \n",
    "    # Start with the style information\n",
    "    html_code = formatter.get_style_defs()\n",
    "    \n",
    "    # Add the pre-wrapped code\n",
    "    html_code += '<pre style=\"line-height: 125%\">'\n",
    "\n",
    "    max_count = max(active_vars_counts.values())\n",
    "    \n",
    "    # Go through each line and wrap it with a span that has a background color corresponding to the number of active vars\n",
    "    for i, line in enumerate(code.splitlines(), 1):\n",
    "        count = active_vars_counts.get(i, 0)\n",
    "        color_intensity = 255 - min(int(200 * count / max_count), 255)  # Scale the color intensity\n",
    "        bg_color = f'rgb(255,{color_intensity},{color_intensity})'  # More vars -> more intense red\n",
    "        html_code += f'<span style=\"background-color: {bg_color}\">{line}\\n</span>'\n",
    "    \n",
    "    html_code += '</pre>'\n",
    "    return html_code\n",
    "\n",
    "# Generate HTML with heatmap\n",
    "html_with_heatmap = generate_heatmap_html(sample_code, active_vars_counts)\n",
    "\n",
    "# Display the HTML in the IPython output cell\n",
    "IPython.display.HTML(html_with_heatmap)\n",
    "# print(html_with_heatmap)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.10.10 ('sweepai-u_CIt3kb-py3.10')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.10"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "25d341f3248a096a89b9dbf6eec8e41f63aed02f6ba059df22a49224e3e8f1b0"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
